package com.dragon.common.modules.file.service.assistants;

import com.dragon.common.base.exceptions.CommonException;
import com.dragon.common.modules.file.common.enums.MediaTypeEnum;
import com.dragon.common.modules.file.domain.dto.SaveFileInfo;
import com.dragon.common.spring.annotations.Assistant;
import com.dragon.utils.IoUtil;
import com.dragon.utils.StringUtil;
import com.dragon.utils.TimeUtil;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Base64;
import java.util.Random;

/**
 * @author Chris Chan
 * Create on 2022/1/9 5:45
 * Use for: 文件业务助手
 * Explain:
 */
@Assistant
public class FileAssistant {
    /**
     * 资源存放位置
     */
    private static final String savePath = "upload";
    @Autowired
    MultipartProperties multipartProperties;

    /**
     * 获得文件媒体类型
     *
     * @param file
     * @return
     */
    public static MediaTypeEnum getMediaType(MultipartFile file) {
        String contentType = file.getContentType();
        return MediaTypeEnum.matchMediaType(contentType);
    }

    /**
     * 类型检查
     * 根据type检查长度
     *
     * @param type
     * @param contentBase64
     * @return
     */
    public boolean checkType(int type, String contentBase64) {
        switch (type) {
            case 0:
            case 1:
            case 2:
            case 3:
            default:
                return true;
        }
    }

    /**
     * 类型检查
     *
     * @param mode
     * @param file
     * @return
     */
    public boolean checkType(int mode, MultipartFile file) {
        if (null == file) {
            throw CommonException.create(1, "文件不能为空");
        }
        return true;
    }

    /**
     * 生成存储文件名
     * type 为1时保留原文件名
     * type 为其他值时生成全局违纪随机文件名，后缀不变
     *
     * @param type
     * @param filename
     * @return
     */

    public String buildSaveFileName(int type, String filename) {
        if (type == 1) {
            return filename;
        }
        return buildRndFileName(getFileExtName(filename));
    }

    /**
     * 构建随机文件名
     *
     * @param extName
     * @return
     */
    public String buildRndFileName(String extName) {
        return "file" + TimeUtil.nowString("yyyyMMddHHmmssSSS") + String.format("%05d", new Random().nextInt(100000)) + extName;
    }

    /**
     * 获取文件名后缀
     *
     * @param fileName
     * @return
     */
    public String getFileExtName(String fileName) {
        String extName = fileName.substring(fileName.lastIndexOf("."));
        return StringUtil.isEmpty(extName) ? ".dat" : extName;
    }

    /**
     * 拼接存储文件路径
     *
     * @param uri
     */
    public String buildSaveFilePath(String uri) {
        return multipartProperties.getLocation() + "/" + uri;
    }

    /**
     * 构建URI
     * 不同的type会放在不同的位置
     * 一般不指定就放在common
     *
     * @param mode
     * @param mediaType
     * @param saveFileName
     */

    public String buildUri(int mode, MediaTypeEnum mediaType, String saveFileName) {
        return "/" + savePath + "/" + mediaType.getSavePath() + "/" + saveFileName;
    }

    /**
     * 参数检查
     *
     * @param mode
     * @param file
     * @return
     */
    public boolean checkParamsForUpload(int mode, MultipartFile file) {
        try {
            if (null == file) {
                throw CommonException.create(1, "文件不能为空");
            }
            //尝试转换并检查type，有错抛异常
            //如果是图片，检查文件尺寸。（这个设计不适用于非图片）
            String contentType = file.getContentType();
            if (StringUtil.isNotEmpty(contentType) && contentType.startsWith("images/")) {
                checkImageDimensions(file, mode);
            }
        } catch (RuntimeException e) {
            throw CommonException.create(1, e.getMessage());
        }
        return true;
    }

    /**
     * 构建存储文件信息
     *
     * @param mode
     * @param mediaType
     * @param fileName
     * @return
     */
    public SaveFileInfo buildSaveFileInfo(int mode, MediaTypeEnum mediaType, String fileName) {
        String saveFileName = buildSaveFileName(mode, fileName);
        String uri = buildUri(mode, mediaType, saveFileName);
        String saveFilePath = buildSaveFilePath(uri);

        return new SaveFileInfo(saveFileName, saveFilePath, uri, uri);

    }

    /**
     * 检查参数
     *
     * @param fileName
     * @param mode
     * @param contentBase64
     * @return
     */
    public boolean checkParamsForUploadBase64(String fileName, int mode, String contentBase64) {
        if (StringUtil.isEmpty(fileName)) {
            throw CommonException.create(1, "缺少文件名");
        }
        if (mode < 0) {
            throw CommonException.create(1, "type取值错误");
        }
        if (StringUtil.isEmpty(contentBase64)) {
            throw CommonException.create(1, "文件内容不能为空");
        }
        return true;
    }

    /**
     * 检查图片文件尺寸
     *
     * @param file
     * @param mode
     * @return
     */
    public boolean checkImageDimensions(File file, int mode) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            return checkImageDimensions(fis, mode);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw CommonException.create(1, e.getMessage());
        } finally {
            IoUtil.close(fis);
        }
    }

    /**
     * 检查图片文件尺寸
     *
     * @param file
     * @param mode
     * @return
     */
    public boolean checkImageDimensions(MultipartFile file, int mode) {
        try {
            byte[] bytes = file.getBytes();
            return checkImageDimensions(bytes, mode);
        } catch (IOException e) {
            e.printStackTrace();
            throw CommonException.create(1, e.getMessage());
        }
    }

    /**
     * 检查图片文件尺寸
     * 读过一次就关闭了
     *
     * @param type
     * @return
     */
    public boolean checkImageDimensions(InputStream inputStream, int type) {
        try {
            byte[] bytes = IOUtils.toByteArray(inputStream);
            return checkImageDimensions(bytes, type);
        } catch (IOException e) {
            e.printStackTrace();
            throw CommonException.create(1, e.getMessage());
        }
    }

    /**
     * 检查图片文件尺寸
     *
     * @param datas
     * @param type
     * @return
     */
    public boolean checkImageDimensions(byte[] datas, int type) {
        switch (type) {
            case 0:
            case 1:
            case 2:
            default:
                return lessThanImageDimensions(datas, 6000, 6000);
        }
    }

    /**
     * 检查图片文件尺寸
     *
     * @param datas
     * @param width
     * @param height
     * @return
     */
    public boolean checkImageDimensions(byte[] datas, int width, int height) {
        return true;
    }

    /**
     * 检查是否小于给定尺寸
     *
     * @param datas
     * @param limitWidth
     * @param limitHeight
     * @return
     */
    public boolean lessThanImageDimensions(byte[] datas, int limitWidth, int limitHeight) {
        BufferedImage image = getImage(datas);
        String info = String.format("imageSize width: %s,height: %s", image.getWidth(), image.getHeight());
        System.out.println(info);
        if (image.getWidth() > limitWidth || image.getHeight() >= limitHeight) {
            throw CommonException.create(1, String.format("图片尺寸长宽要求 %s x %s", limitWidth, limitHeight));
        }
        return true;
    }

    /**
     * 检查是否等于给定尺寸
     *
     * @param datas
     * @param width
     * @param height
     * @return
     */
    public boolean equalThanImageDimensions(byte[] datas, int width, int height) {
        BufferedImage image = getImage(datas);
        System.out.println(String.format("width: %s,height: %s", image.getWidth(), image.getHeight()));
        if (image.getWidth() != width || image.getHeight() != height) {
            throw CommonException.create(1, String.format("图片尺寸要求 %s x %s", width, height));
        }
        return true;
    }

    /**
     * 检查文件尺寸是否小于给定值
     * 单位:字节
     *
     * @param datas
     * @param limitSize
     * @return
     */
    public boolean lessThanFileSize(byte[] datas, int limitSize) {
        return datas.length <= limitSize;
    }

    /**
     * 获取一个图片文件的尺寸
     *
     * @param datas
     * @return
     */
    public Dimension getDimensions(byte[] datas) {
        BufferedImage image = getImage(datas);
        return new Dimension(image.getWidth(), image.getHeight());
        //LoggerUtil.get(this).debug(String.format("width: %s,height: %s", image.getWidth(), image.getHeight()));
    }

    /**
     * 获取一个图片对象
     *
     * @param datas
     * @return
     */
    public BufferedImage getImage(byte[] datas) {
        BufferedImage image = null;
        try {
            return ImageIO.read(new ByteArrayInputStream(datas));
        } catch (IOException e) {
            e.printStackTrace();
            throw CommonException.create(1, e.getMessage());
        }
    }

    /**
     * 修正Base64字节数组异常数据
     *
     * @param contentBytes
     * @return
     */
    public byte[] correctBase64Bytes(byte[] contentBytes) {
        if (null == contentBytes) {
            return null;
        }
        for (int i = 0, len = contentBytes.length; i < len; i++) {
            if (contentBytes[i] < 0) {
                contentBytes[i] += 256;
            }
        }
        return contentBytes;
    }

    /**
     * 解析Base64
     *
     * @param contentBase64
     * @return
     */
    public byte[] parseBase64(String contentBase64) {
        //去掉数据头
        int index = contentBase64.lastIndexOf(",");
        String substring = contentBase64.substring(index + 1);
        //替换掉换行符
        String replaceAll = substring.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
        // https://blog.csdn.net/weixin_43710224/article/details/88528056
        byte[] contentBytes = Base64.getDecoder().decode(replaceAll);
        //修正异常数据
        return correctBase64Bytes(contentBytes);
    }
}
